Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoupling file excludability from mutant spans #2645

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

psfinaki
Copy link
Contributor

@psfinaki psfinaki commented Aug 18, 2023

Phew... Okay so this is a somewhat bigger steps towards the F# support.

Motivation

F# has its own understanding of text documents (btw that's sad). Roslyn (in this context C#) generally counts all characters from zero whereas F# generally uses lines and columns. Of course one is convertible to the other, it's more about what's native to the compiler. Type-wise, it's Microsoft.CodeAnalysis.Text.TextSpan versus FSharp.Compiler.Text.Range.

Sooner or later we would need native span arithmetics for mutants hence one approach is to create a definition of mutant span on the user level (= configuration level) and then translate them to compiler definitions of spans based on the file type.

Implementation

This PR

  • Makes FilePattern language-agnostic
  • Splits the concept of excludability to its own place since that concept has some usage on its own
  • Adds some span (range) arithmetics for F#
  • Hopefully brings some collateral improvements

TO DO

  • tests
  • basic parameter validation for new types
  • docs
  • see comments in the PR

@psfinaki psfinaki marked this pull request as draft August 18, 2023 20:52
return pattern.MutantSpans.Any(span => RangeModule.rangeContainsRange(FromMutantSpan(span), range));
}

public override IEnumerable<Range> Reduce(IEnumerable<Range> spans)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually much will be probably taken from the TextSpanHelper, eventually using the same model as is done for span matching.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the TextSpanHelper be the abstraction for span and range?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well as of now TextSpanHelper is a set of extensions on top of TextSpan (C#) whereas RangeHelper is a set of extensions on top of Range (F#). But let me see if I can refactor it a bit, there is some common code to extract.


namespace Stryker.Core.ProjectComponents
{
public class SimpleFileLeaf: ProjectComponent, IReadOnlyFileLeaf
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really needed only GitDiffProvider. Although it's useful in tests also.

Now, I guess this could be somehow inserted in the tree of abstractions here since there is some logic duplication with ExcludableProjectComponent. But my brain already hurts from abstractions here and the duplication is rather small anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what this class is doing.. Is this a non-language-specific file? Should this be an abstract class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this is a non-language specific file. It's initiated in the GitDiffProvider where we indeed don't care about the code:

private void RemoveFilteredOutFiles(DiffResult diffResult)
{
    foreach (var glob in new SimpleFileLeaf(_options.DiffIgnoreChanges).Patterns.Select(d => d.Glob))
    {
        diffResult.ChangedSourceFiles = diffResult.ChangedSourceFiles.Where(diffResultFile => !glob.IsMatch(diffResultFile)).ToList();
        diffResult.ChangedTestFiles = diffResult.ChangedTestFiles.Where(diffResultFile => !glob.IsMatch(diffResultFile)).ToList();
    }
}

@@ -78,7 +78,13 @@ private void DisplayComponent(IReadOnlyProjectComponent inputComponent, Table ta

var mutationScore = inputComponent.GetMutationScore();

if (inputComponent.IsComponentExcluded(_options.Mutate))
var isExcluded = inputComponent switch
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to understand this... is this correct? Can a folder be excluded in the context of clear text reporters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, with globbing it's possible to exclude a whole folder. So it will be displayed as excluded. However, now that you mention this, we moved from exclude to ignore as the preferred term. So maybe we need to display [Ignored] instead of [Excluded].

@psfinaki
Copy link
Contributor Author

Unit tests pass, there are two integration tests failing, I will look into them.

Maintainers - please, before I continue on this. Do you think this is a reasonable design change? Is the current change something you approve "in principle"? :)

@psfinaki
Copy link
Contributor Author

psfinaki commented Sep 1, 2023

Ping @richardwerkman @rouke-broersma @dupdob :)

Copy link
Member

@richardwerkman richardwerkman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is a reasonable design change? Is the current change something you approve "in principle"? :)

I think this is generally going in the right direction. I have some questions regarding the abstractions, but those are kind of a mess anyway.

We could also choose to not implement some of the deeper features in F#, like ignoring part of a file. If this makes the transition to F# easier. We can always implement it later

@@ -78,7 +78,13 @@ private void DisplayComponent(IReadOnlyProjectComponent inputComponent, Table ta

var mutationScore = inputComponent.GetMutationScore();

if (inputComponent.IsComponentExcluded(_options.Mutate))
var isExcluded = inputComponent switch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, with globbing it's possible to exclude a whole folder. So it will be displayed as excluded. However, now that you mention this, we moved from exclude to ignore as the preferred term. So maybe we need to display [Ignored] instead of [Excluded].

return pattern.MutantSpans.Any(span => RangeModule.rangeContainsRange(FromMutantSpan(span), range));
}

public override IEnumerable<Range> Reduce(IEnumerable<Range> spans)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the TextSpanHelper be the abstraction for span and range?


namespace Stryker.Core.ProjectComponents
{
public class SimpleFileLeaf: ProjectComponent, IReadOnlyFileLeaf
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what this class is doing.. Is this a non-language-specific file? Should this be an abstract class?

@@ -0,0 +1,17 @@
namespace Stryker.Core.ProjectComponents
{
public class ExcludableString
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this class do? And is this cSharp specific?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this is now basically just a non-language-specific construct to represent the idea that we can include or exclude files in some code flows. Distinguish Person.cs vs !Person.cs or Person.fs vs Person.fs. E.g. DiffIgnoreChangesInput don't need info about spans as opposed to MutateInput.

@psfinaki psfinaki marked this pull request as ready for review September 21, 2023 18:38
@psfinaki
Copy link
Contributor Author

Okay sorry for the delay, I am back on this. Now tests pass, int tests pass, good stuff. Let me see if I can refactor things a bit and add some extra tests, I will let you know then :)

@psfinaki
Copy link
Contributor Author

Okay, so this one is getting quite big and I promised small PRs :)

I extracted the changes to the following PR:

Let's address those ones first and then see what's left here. Meanwhile converting to draft again.

@psfinaki psfinaki marked this pull request as draft September 23, 2023 17:22
@rouke-broersma
Copy link
Member

F# has its own understanding of text documents (btw that's sad). Roslyn (in this context C#) generally counts all characters from zero whereas F# generally uses lines and columns. Of course one is convertible to the other, it's more about what's native to the compiler. Type-wise, it's Microsoft.CodeAnalysis.Text.TextSpan versus FSharp.Compiler.Text.Range.

Sooner or later we would need native span arithmetics for mutants hence one approach is to create a definition of mutant span on the user level (= configuration level) and then translate them to compiler definitions of spans based on the file type.

This is more relevant than ever, due to work being done on a mutation server protocol for driving mutation testing from external sources such as IDE plugins. The protocol supports mutant spans, and defines them as 1-based. It would be practical to take mutant spans as 1-based in configuration and then convert them to whatever csharp and/or f# needs.

@psfinaki
Copy link
Contributor Author

Sorry for being a very bad contributor recently 😢 my life was quite turbulent until very recently.

Things are settling up tho, also I got a lot of F# knowledge meanwhile and hope to get back on track with this during gray autumn and dark winter evenings.

@rouke-broersma
Copy link
Member

Sorry for being a very bad contributor recently 😢 my life was quite turbulent until very recently.

Things are settling up tho, also I got a lot of F# knowledge meanwhile and hope to get back on track with this during gray autumn and dark winter evenings.

No worries, we haven't kicked you out yet ;D Life happens, hope all's well for you soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants